package IC.lir;
import java.util.List;

import IC.lir.operands.Label;
import IC.lir.operands.Operand;

import IC.lir.decl.DispatchVector;
import IC.lir.decl.LabelDecl;
import IC.lir.decl.StringDecl;
import IC.lir.instructions.*;
import util.Pair;

/**
 * Converts a LIR instruction to a string
 */
public class LIRPrinter implements Visitor<Object,String> {

	public static void Print(List<LIREntry> lir) {
		LIRPrinter p = new LIRPrinter();
		for (LIREntry e: lir) {
			System.out.println(e.accept(p, null));
		}
	}
	
	/**
	 * ArrayLength
	 */
	public String visit(ArrayLengthInstr that, Object context) {
		return "\tArrayLength\t"+that.getArray()+", "+that.getDest();
	}

	/**
	 * JumpXXX 
	 */
	public String visit(JumpInstr that, Object context) {
		switch (that.getCond()) {
		case TRUE:
			return "\tJumpTrue\t" + that.getLabel();
		case FALSE:
			return "\tJumpFalse\t" + that.getLabel();
		case GREATER:
			return "\tJumpG\t\t" + that.getLabel();
		case GREATEREQUAL:
			return "\tJumpGE\t\t" + that.getLabel();
		case LESSTHAN:
			return "\tJumpL\t\t" + that.getLabel();
		case LESSTHANEQUAL:
			return "\tJumpLE\t" + that.getLabel();
		case UNCONDITIONAL:
			return "\tJump\t\t" + that.getLabel();
		default:
			return null;
		}
	}

	/**
	 * Library
	 */
	public String visit(LibraryInstr that, Object context) {
		StringBuffer s = new StringBuffer("\tLibrary\t\t");
		s.append("__"+that.getFuncName());
		s.append("(");
		boolean first = true;
		for (Operand op : that.getParams()) {
			if (!first) s.append(", ");
			s.append(op);
			first = false;
		}
		s.append("), ");
		s.append(that.getDest());
		return s.toString();
	}

	/**
	 * Add, Sub, Neg, Not, Or, Xor, Mul, Div, Mov, Compare
	 */
	public String visit(BasicInstr that, Object context) {
		return "\t"+that.getOp()+"\t\t"+
			((that.getSource()!=null)?(that.getSource()+", "):"")+that.getDest();
	}

	/**
	 * Move
	 */
	public String visit(MoveInstr that, Object context) {
		return "\tMove\t\t"+that.getSource()+", "+that.getDest();
	}

	/**
	 * Move -> Array
	 */
	public String visit(MoveToArrayInstr that, Object context) {
		return "\tMoveArray\t"+	that.getSource()+", "+
					that.getArray()+"["+that.getIndex()+"]";
	}

	/**
	 * Move <- Array
	 */
	public String visit(MoveFromArrayInstr that, Object context) {
		return "\tMoveArray\t"+that.getArray()+"["+that.getIndex()+"], "
					+that.getDest();
	}

	/**
	 * Move -> Field
	 */
	public String visit(MoveToFieldInstr that, Object context) {
		return "\tMoveField\t"+that.getSource()+", "+
					that.getInstance()+"."+that.getField();
	}

	/**
	 * Move <- Field
	 */
	public String visit(MoveFromFieldInstr that, Object context) {
		return "\tMoveField\t"+that.getInstance()+"."+that.getField()+", "+
					that.getDest();
	}

	/**
	 * Return
	 */
	public String visit(ReturnInstr that, Object context) {
		return "\tReturn\t\t"+that.getValue();
	}

	/**
	 * StaticCall
	 */
	public String visit(StaticCallInstr that, Object context) {
		StringBuffer s = new StringBuffer("\tStaticCall\t");
		s.append(that.getMethodLabel().getName());
		s.append("(");
		boolean first = true;
		for (Pair<String,Operand> op : that.getArgs()) {
			if (!first) s.append(", ");
			s.append(op.getFirst());
			s.append("=");
			s.append(op.getSecond());
			first = false;
		}
		s.append("), ");
		s.append(that.getDest());
		return s.toString();
	}

	/**
	 * VirtulCall
	 */
	public String visit(VirtualCallInstr that, Object context) {
		StringBuffer s = new StringBuffer("\tVirtualCall\t");
		s.append(that.getObject());
		s.append(".");
		s.append(that.getMethod());
		s.append("(");
		boolean first = true;
		for (Pair<String,Operand> op : that.getArgs()) {
			if (!first) s.append(", ");
			s.append(op.getFirst());
			s.append("=");
			s.append(op.getSecond());
			first = false;
		}
		s.append("), ");
		s.append(that.getDest());
		return s.toString();
	}


	/**
	 * Label declaration
	 */
	public String visit(LabelDecl that, Object d) {
		return that.getLabel()+":";
	}

	
	/**
	 * String declaration
	 */
	public String visit(StringDecl that, Object d) {
		return "\t"+that.getName() + ":\t\t\t\"" + 
				that.getValue().replace("\\", "\\\\")
								.replace("\"", "\\\"")
								.replace("\n", "\\n")
								.replace("\t", "\\t")+ "\"";
	}

	
	/**
	 * Dispatch vector declaration
	 */
	public String visit(DispatchVector that, Object d) {
		boolean first = true;
		StringBuffer ret = new StringBuffer(that.getName());
		ret.append(":\t\t[  ");
		for (Label label: that.getLabels()) {
			if (!first) ret.append(", ");
			ret.append(label.getName());
			first = false;
		}
		ret.append("  ]");
		return ret.toString();
	}
}
